using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SolarWinds.Helpers;

namespace SolarWinds.Effects
{
    public class LensFlare : DrawableGameComponent
    {
        public struct Spot
        {
            public float Position;
            public float Size;
            public float Rotation;
            public Color Color;
            public Point TileIndex;

            public Spot(SpotType type, float position, float rotation, float size, Color color)
            {
                TileIndex = Type2TileIndex(type);
                Position = position;
                Rotation = rotation;
                Size = size;
                Color = color;
            }

            private static Point Type2TileIndex(SpotType type)
            {
                switch (type)
                {
                    case SpotType.Sun: return new Point(0, 0);
                    case SpotType.Glow: return new Point(1, 0);
                    case SpotType.Halo: return new Point(2, 0);
                    case SpotType.Circle: return new Point(3, 0);
                    case SpotType.Flare: return new Point(0, 1);
                    case SpotType.Ring: return new Point(1, 1);
                    case SpotType.Streaks: return new Point(2, 1);
                    default: return new Point(0, 0);
                }
            }
        }

        public enum SpotType
        {
            Sun,
            Glow,
            Halo,
            Circle,
            Flare,
            Ring,
            Streaks
        }

        private readonly string mTextureAsset = "lensflare";
        private Texture2D mTexture;

        private readonly List<Spot> mSpots = new List<Spot>();

        private Vector3 mLightPosition = Vector3.Zero;

        private SpriteBatch mSpriteBatch;

        public Vector3 LightPosition
        {
            get { return mLightPosition; }
            set { mLightPosition = value; }
        }	

        public LensFlare(Game game, string asset)
            : base(game)
        {
            mTextureAsset = asset;
        }

        public void AddSpot(Spot spot)
        {
            mSpots.Add(spot);
        }

        public override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);

            if (SolarWindsGame.Camera.ViewFrustum.Contains(LightPosition) == ContainmentType.Disjoint) return;

            GraphicsDevice device = GraphicsDevice;

            int height = device.PresentationParameters.BackBufferHeight;
            int width = device.PresentationParameters.BackBufferWidth;

            Vector3 lightPosScreen = device.Viewport.Project(mLightPosition,
                                                             SolarWindsGame.Camera.Projection,
                                                             SolarWindsGame.Camera.View, Matrix.Identity);

            float flareSize = (0.7f * height) / width;

            // center of the screen
            var center = new Vector2(width / 2.0f, height / 2.0f);
            // flare base line
            Vector2 flareDir = center - new Vector2(lightPosScreen.X, lightPosScreen.Y);

            // Check if origin is on screen, fade out at borders
            float alpha = 1.0f;
            float distance = Math.Max(Math.Abs(flareDir.X), Math.Abs(flareDir.Y));
            float safeFrame = height / 2.25f;
            if (distance > safeFrame)
            {
                distance -= safeFrame;
                // If distance is more than half the resolution, don't show anything!
                if (distance > safeFrame) return;

                alpha = 1.0f - (distance / safeFrame);
                
                if (alpha > 1.0f)
                    alpha = 1.0f;
            }

            mSpriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.BackToFront, SaveStateMode.SaveState);

            var tileSize = new Point(128, 128);
            var tileOrigin = new Vector2(tileSize.X *0.5f, tileSize.Y * 0.5f);
            var flarePos = new Vector2(lightPosScreen.X, lightPosScreen.Y);

            foreach (Spot spot in mSpots)
            {
                Vector4 flareCol = spot.Color.ToVector4() * alpha;

                mSpriteBatch.Draw(mTexture, flarePos + spot.Position * flareDir,
                                  TileHelper.TileIndex2Rectangle(spot.TileIndex, tileSize),
                                  new Color(flareCol), spot.Rotation, tileOrigin, spot.Size * flareSize * alpha, SpriteEffects.None, 0);
            }
            
            mSpriteBatch.End();
        }

        protected override void LoadContent()
        {
            base.LoadContent();

            GraphicsDevice device = GraphicsDevice;

            mTexture = Game.Content.Load<Texture2D>(@"Textures\" + mTextureAsset);

            mSpriteBatch = new SpriteBatch(device);
        }        
    }
}